<?php
namespace Sfsn\Core;

use Sfsn\Core\Config;
use Sfsn\Core\Controller\AutoLoad;
use Sfsn\Core\Controller\Factory;
use Sfsn\Core\Util\HtmlCache;
use Sfsn\Core\Exception\Exception;
use Sfsn\Core\Util\Log;

class App
{

    /**
     * request对象
     *
     * @var unknown
     */
    public static $request;

    /**
     * route对象
     *
     * @var unknown
     */
    public static $route;

    /**
     * 分组名称
     *
     * @var unknown
     */
    public static $group = '';

    /**
     * 模块名称
     *
     * @var unknown
     */
    public static $module = '';

    /**
     * 操作名称
     *
     * @var unknown
     */
    public static $action = '';

    /**
     * 执行模块，单一入口控制核心
     */
    public static function run()
    {
        // 基础运行配置
        self::loadFile(); // 基础文件载入
        self::config(); // 初始化配置
        self::_debug(); // debug设置
        self::autoload(); // 实现类的自动加载
        self::utilInit(); // 初始化log
        self::object(); // 初始化需要用到的对象
        self::timezone(); // 初始化时区
        try {               
            // 网址解析
            if (function_exists('url_parse_ext')) {
                url_parse_ext(); // 自定义网址解析
            } else {
                self::parseUrl(); // url解析
            }                
            //合并分组配置
            self::setGroupConfig();
            // 常量定义
            self::_define();
            
            // 如果开启静态页面缓存，则尝试读取静态缓存
            if (false == self::_readHtmlCache()) {
                // 静态缓存读取失败，执行模块操作
                self::_execute();
            }
            
            // 如果存在回调函数app_end，则在即将结束前调用
            if (function_exists('app_end')) {
                app_end();
            }
        } catch (Exception $e) {            
            Exception::show($e);
        }
    }

    /**
     * 获取config如果传入group则获取合并后的数组
     * 
     * @param string $group            
     * @return array|unknown
     */
    public static function appConfig()
    {
        return Config::get('APP');        
    }
    /**
     * 合并分组的配置
     */
    public static function setGroupConfig()
    {
        $group = self::$group; 
        $config = Config::get('APP');
        if ($group && isset($config['GROUP_LIST'][$group])) {            
            $config = array_merge($config, $config['GROUP_LIST'][$group]);
            Config::set('APP',$config);
        }
    }

    /**
     * 基础文件载入
     */
    public static function loadFile()
    {
        $path = SFSN_PATH;
        // 加载配置类
        require ($path . 'Core/Config.php');
        // 加载自动载入类
        require ($path . 'Core/Controller/AutoLoad.php');
        // 加载常用函数库
        require ($path . 'Lib/Common.function.php');
        // 加载字符串处理类库
        require ($path . 'Lib/String.function.php');
        // 加载扩展函数库
        require ($path . 'Ext/extend.php');
    }

    /**
     * 初始化配置
     */
    public static function config()
    {
        $file = APP_CONFIG_FILE;
        // 项目配置初始化
        $app = Config::get('APP'); // 全局配置
        $controller = Config::get('CONTROLLER'); // 控制器相关配置
        $diy = require_once $file; // 载入app配置        
        Config::set('APP', array_merge($app, $controller, $diy)); // 更新全局配置
    }

    /**
     * debug设置
     */
    private static function _debug()
    {
        $config = self::appConfig();
        $debug = $config['DEBUG'];
        
        define('DEBUG', $debug);
        // 调试模式开关
        if ($debug) {
            ini_set("display_errors", 1);
            error_reporting(E_ALL ^ E_NOTICE); // 除了notice提示，其他类型的错误都报告
        } else {
            ini_set("display_errors", 0);
            error_reporting(0); // 把错误报告，全部屏蔽
        }
    }

    /**
     * 实现类的自动加载
     *
     * @param unknown $classname            
     * @return boolean
     */
    public static function autoload()
    {
        $config = self::appConfig();
        // 自动载入目录
        $loadDir = [
            dirname(SFSN_PATH) . DIRECTORY_SEPARATOR, // 框架上级目录，便于根据命名空间组成文件载入路径
            APP_PATH
        ];
        /**
         * 自动载入目录
         */
        $loadDir = array_merge($loadDir, $config['AUTOLOAD_DIR']);
        // 自动载入注册
        AutoLoad::registerAutoload($loadDir);
    }

    /**
     * 初始化一些类配置
     */
    public static function utilInit()
    {
        $config = self::appConfig();
        Log::init($config); // log初始化
        Exception::init($config); // 异常初始化
    }

    /**
     * 初始化需要用到的对象
     */
    public static function object()
    {
        self::$request = Factory::create('request');
        self::$route = Factory::create('route');
    }

    /**
     * 设置时区
     */
    public static function timezone()
    {
        $config = self::appConfig();
        date_default_timezone_set($config['DATE_TIMEZONE']);
    }

    /**
     * url解析
     */
    public static function parseUrl()
    {
        $route = self::$route;
        $request = self::$request;
        $config = self::appConfig();
        // 进行配置
        $route->setConfig($config);
        // 设置请求对象
        $route->setRequest($request);
        
        if ($config['URL_TYPE'] < 3) { // 原生url
            /*
             * 0 index.php?g=group&m=module&a=action
             * 1 ?g=group&m=module&a=action
             */
            $route->gmaUrl();
        } else { // 采用pathinfo方式
            /*
             * 2 index.php/group/module/action
             * 3 /group/module/action
             * 4 index.php?r=/group/module/action
             * 5 ?r=/group/module/action
             */
            $route->parseUrl(); // 解析模块和操作
        }
        $routeDate = $route->getData();
        self::$group = $routeDate['group'];
        self::$module = $routeDate['module'];
        self::$action = $routeDate['action'];
        // 将路由解析数据传给request，可以通过
        $request->setRoute($routeDate);
    }
    
    // 常量定义
    private static function _define()
    {
        $group = self::$group;
        $request = self::$request;
        $config = self::appConfig();
        
        // 取得当前执行文件，带路径
        $script_name = $request->getScriptName();
        // __ROOT__和__PUBLIC__常用于图片，css，js定位，__APP__和__URL__常用于网址定位
        // 去掉文件名得到目录
        $root = str_replace(basename($script_name), '', $script_name);
        // 如果没有设置APP_PATH则进行设置,此变量为项目执行文件index.php的所在目录
        if (! defined('APP_PATH'))
            define('APP_PATH', getcwd().DIRECTORY_SEPARATOR);
        
            // 当前入口所在的目录，后面不带 "/"
        define('__ROOT__', substr($root, 0, - 1));
        // 取得目录名称组成公用文件夹路径，用于模板中
        define('__PUBLIC__', __ROOT__ . '/' . basename($config['PUBLIC_PATH']));
        
        // 如果奇数，则网址不包含入口文件名，如index.php，偶数则带入口文件
        if ($config['URL_TYPE'] % 2 == 1) {
            define('__APP__', $script_name); // 当前入口文件
        } else {
            define('__APP__', __ROOT__);
        }
        
        // 定义分组常量
        define('__GROUP__', self::$group);
        // 定义模块常量
        define('__MODULE__', self::$module);
        // 定义方法常量
        define('__ACTION__', self::$action);
    }    
    
    /**
     * 取得模型的路径
     * @return string
     */
    private static function _modulePath()
    {
        $config = self::appConfig();
        if($config['GROUP_DEFAULT']){
            $group = self::$group;
            $path = $config['GROUP_LIST'][$group]['MODULE_PATH'];
            empty($path) && $path = $config['MODULE_PATH'] . $group . '/' ;
        }else {
            $path = $config['MODULE_PATH'];
        }
        return $path;
    }
    /**
     * 执行操作
     */
    private static function _execute()
    {             
        $config = self::appConfig();
        
        $module = self::$module . $config['MODULE_SUFFIX'];
        $action = self::$action . $config['ACTION_SUFFIX'];
        $request = self::$request;        
        $path = self::_modulePath();
        
        // 过滤字符
        $namespace = str_replace([
            '../',
            './'
        ], '', $path);
        // 保证首尾都有/
        $namespace = '/' . $namespace . '/';
        // 替换重复//
        $namespace = str_replace('//', '/', $namespace);
        // 转换/dir/dir/classname/ 为 \dir\dir\classname\成为命名空间的格式
        $namespace = str_replace('/', '\\', $namespace);
        // 命名空间与类名合并
        $spaceClass = $namespace . $module;
        // 检测类是否存在
        if (! class_exists($spaceClass)) {
            throw new Exception($spaceClass . '不存在');
        }
        // 实例化模块对象
        $object = new $spaceClass();
        // 检测方法是否存在
        if (method_exists($object, $action) || method_exists($object, '__call')) {
            // 执行方法
            $object->$action($request);
        }else{
            //抛出一场
            throw new Exception($spaceClass . '中找不到' . $action . '()');
        }
        
        
        // 如果缓存开启，写入静态缓存，只有符合规则的，才会创建缓存
        self::_writeHtmlCache();
    }

    /**
     * 读取静态页面缓存
     *
     * @param string $module            
     * @param string $action            
     * @return boolean
     */
    private static function _readHtmlCache()
    {
        $group = self::$group;
        $module = self::$module;
        $action = self::$module;
        $config = self::appConfig();
        if ($config['HTML_CACHE_ON']) {
            return HtmlCache::read($module, $action);
        }
        return false;
    }

    /**
     * 写入静态页面缓存
     */
    private static function _writeHtmlCache()
    {
        $config = self::appConfig();
        if ($config['HTML_CACHE_ON']) {
            HtmlCache::write();
        }
    }
}